package com.ruoyi.framework.config;

import com.ruoyi.framework.config.properties.PermitAllUrlProperties;
import com.ruoyi.framework.security.SmsAuthenticationProvider;
import com.ruoyi.framework.security.filter.JwtAuthenticationTokenFilter;
import com.ruoyi.framework.security.handle.AuthenticationEntryPointImpl;
import com.ruoyi.framework.security.handle.LogoutSuccessHandlerImpl;
import com.ruoyi.framework.web.service.app.H5UserDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutFilter;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.web.filter.CorsFilter;

import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;

/**
 * H5端Security配置
 * 与后台登录配置隔离，独立处理H5接口的认证与授权
 */
@EnableMethodSecurity(securedEnabled = true)
@Configuration
@Order(1) // 优先级高于后台配置（值越小优先级越高）
public class H5SecurityConfig {

    /**
     * H5用户详情服务（从h5_user表查询用户）
     */
    @Autowired
    private H5UserDetailsService h5UserDetailsService;

    /**
     * 认证失败处理类（可复用后台的实现，也可自定义）
     */
    @Autowired
    private AuthenticationEntryPointImpl unauthorizedHandler;

    /**
     * H5退出处理类（可复用或自定义）
     */
    @Autowired
    private LogoutSuccessHandlerImpl logoutSuccessHandler;

    /**
     * JWT过滤器（复用现有过滤器，需确保能兼容H5的token解析）
     */
    @Autowired
    private JwtAuthenticationTokenFilter authenticationTokenFilter;

    /**
     * 跨域过滤器（复用全局配置）
     */
    @Autowired
    private CorsFilter corsFilter;

    @Autowired
    private BCryptPasswordEncoder bCryptPasswordEncoder;

    /**
     * 允许匿名访问的地址
     */
    @Autowired
    private PermitAllUrlProperties permitAllUrl;

    /**
     * H5专用认证管理器
     * 使用H5UserDetailsService处理H5用户的认证
     */
    @Bean
    public AuthenticationManager h5AuthenticationManager() {
        // 1. 原有密码认证提供者（保留）
        DaoAuthenticationProvider passwordProvider = new DaoAuthenticationProvider();
        passwordProvider.setUserDetailsService(h5UserDetailsService);
        passwordProvider.setPasswordEncoder(bCryptPasswordEncoder);
        // 2. 新增短信认证提供者
        SmsAuthenticationProvider smsProvider = new SmsAuthenticationProvider(h5UserDetailsService);

        // 3. 组合两个提供者
        return new ProviderManager(Arrays.asList(smsProvider, passwordProvider));
    }

    /**
     * H5专用过滤器链
     * 只处理/h5/**路径的请求，与后台接口权限隔离
     */
    @Bean
    public SecurityFilterChain h5FilterChain(HttpSecurity http) throws Exception {
        http.requestMatcher(request -> request.getRequestURI().startsWith("/h5/"))
                // CSRF禁用（同后台，基于token无状态）
                .csrf(AbstractHttpConfigurer::disable)
                // 禁用缓存
                .headers(headers -> headers
                        .cacheControl(HeadersConfigurer.CacheControlConfig::disable)
                        .frameOptions(HeadersConfigurer.FrameOptionsConfig::sameOrigin)
                )
                // 认证失败处理
                .exceptionHandling(exception -> exception
                        .authenticationEntryPoint(unauthorizedHandler)
                )
                // 无状态会话（不创建session）
                .sessionManagement(session -> session
                        .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                )
                // H5接口权限配置
                .authorizeHttpRequests(auth -> {
                    permitAllUrl.getUrls().forEach(url -> auth.antMatchers(url).permitAll());
                    auth.antMatchers("/**").permitAll()
                            // H5登录、注册、验证码等接口允许匿名访问
                            .antMatchers("/h5/login", "/h5/register", "/captchaImage").permitAll()
                            // H5静态资源允许匿名访问（如H5页面、图片等）
                            .antMatchers(HttpMethod.GET, "/h5/static/**", "/h5/images/**").permitAll()
                            // 其他所有H5接口需要H5角色认证
                            .antMatchers("/h5/**").hasRole("H5")
                            // 除上述外的请求无需处理（由后台过滤器链处理）
                            .anyRequest().authenticated();
                })
                // H5退出配置
                .logout(logout -> logout
                        .logoutUrl("/h5/logout") // H5专用退出接口
                        .logoutSuccessHandler(logoutSuccessHandler)
                )
                // 添加JWT过滤器（在用户名密码过滤器之前）
                .addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class)
                // 添加跨域过滤器（在JWT过滤器之前）
                .addFilterBefore(corsFilter, JwtAuthenticationTokenFilter.class)
                .addFilterBefore(corsFilter, LogoutFilter.class);

        return http.build();
    }
}